﻿//using System;
//using System.Collections.Generic;
//using System.Data;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//using NAudio.Dsp;

//namespace Orpheus.Analyzer
//{
//    public class CombFilterTempoAnalyzer: TrackAnalyserBase
//    {
//        private List<Tuple<float, float>> samples;
//        private bool calculated = false;

//        public CombFilterTempoAnalyzer(TrackInfo trackInfo, GetCurrentTimeDelegate getCurrentTimeCallback) 
//            : base(trackInfo, getCurrentTimeCallback)
//        {
//            samples = new List<Tuple<float, float>>();
//            _trackInfo.Tempo = new TimeStepFunction<double>();
//        }

//        bool IsPowerOfTwo(int x)
//        {
//            return (x & (x - 1)) == 0;
//        }

//        public override void ProcessSamples(float left, float right)
//        {
//            if (left > 1f || right > 1f)
//            {
//                throw new InvalidConstraintException();
//            }

//            samples.Add(new Tuple<float, float>(left, right));
//            int m = (int) Math.Log(samples.Count, 2);
//            if (_GetCurrentTimeCallback().TotalSeconds > 5  && IsPowerOfTwo(samples.Count) && !calculated)
//            {
//                int N = samples.Count;
//                Complex[] fft = new Complex[N];

//                int[] bandLimits = { 0, 200, 400, 800, 1600, 3200 };
//                int maxFreq = 4096;

//                for (int i = 0; i < N; i++)
//                {
//                    fft[i].X = (float)(samples[i].Item1 * FastFourierTransform.HammingWindow(i, fft.Length));
//                    fft[i].Y = 0;//(float)(samples[i].Item2 * FastFourierTransform.HammingWindow(i, fft.Length));
//                }

//                FastFourierTransform.FFT(true, m, fft);

//                List<int> bl = new List<int>();
//                List<int> br = new List<int>();
//                for (int i = 0; i < bandLimits.Length - 1; i++ )
//                {
//                    bl.Add((int)Math.Floor(1.0 * bandLimits[i] / maxFreq * N /2)); //?
//                    br.Add((int) Math.Floor(1.0*bandLimits[i + 1]/maxFreq*N/2) - 1); //?
//                }

//                bl.Add((int)Math.Floor(1.0 * bandLimits.Last() / maxFreq * N / 2) + 1);
//                br.Add(N / 2);

//                List<Complex[]> bands = new List<Complex[]>();
//                for (int i = 0; i < bandLimits.Length; i++ )
//                {
//                    Complex[] band = new Complex[N];
//                    for (int j = bl[i]; j < br[i]; j++)
//                    {
//                        band[j] = fft[j];
//                    }

//                    for (int j = N - br[i]; j < N - bl[i]; j++)
//                    {
//                        band[j] = fft[j];
//                    }

//                    bands.Add(band);
//                }

//                List<float[]> diffBands = new List<float[]>();
//                foreach (var band in bands)
//                {
//                    diffBands.Add(band.Select(c => c.X).ToArray());
//                } 
               

//                ////выпрямление сигнала
//                //for (int i = 0; i < bands.Count; i++ )
//                //{
//                //    Complex[] band = bands[i];
//                //    FastFourierTransform.FFT(false, (int)Math.Log(band.Length, 2), band);

//                //    for (int j = 0; j < band.Length; j++)
//                //    {
//                //        band[j].X = band[j].X < 0 ? -band[j].X : band[j].X;
//                //        band[j].Y = 0;
//                //    }

//                //   // FastFourierTransform.FFT(true, (int)Math.Log(band.Length, 2), band);
//                //}

//                ////дифференциация сигнала

//                //List<float[]> diffBands = new List<float[]>(); 
//                //for (int i = 0; i < bands.Count; i++ )
//                //{
//                //    Complex[] band = bands[i];
//                //    float[] diffBand = new float[band.Length];
//                //    for (int j = 1; j < band.Length; j++)
//                //    {
//                //        var d = band[j].X - band[j - 1].X;

//                //        if (d > 0)
//                //        {
//                //            diffBand[j] = d;
//                //        }
//                //    }

//                //    diffBands.Add(diffBand);
//                //}

//                int bmpMax = DetectTempo(diffBands, 60, 200, 10, maxFreq);

//                //for (int bmp = 40; bmp < 200; bmp += 10)
//                //{
//                //    int Ti = (int)(60.0 / bmp * 44100);

//                //    float[] l = new float[N];
//                //    float[] j = new float[N];
//                //    for (int k = 0; k < N; k += Ti)
//                //    {
//                //        l[k] = j[k] = (float)Math.Pow(2, 15);
//                //    }

//                //    Complex[] fftImpulses = new Complex[N];
//                //    for (int i = 0; i < N; i++)
//                //    {
//                //        fftImpulses[i].X = (float)(l[i] * FastFourierTransform.HammingWindow(i, fftImpulses.Length));
//                //        fftImpulses[i].Y = 0;// (float)(j[i] * FastFourierTransform.HammingWindow(i, fft.Length));
//                //    }

//                //    FastFourierTransform.FFT(true, m, fftImpulses);

//                //    double E = 0;
//                //    for (int i = 0; i < N; i++)
//                //    {
//                //        E += fft[i].Multiply(fftImpulses[i]).Abs();
//                //    }

//                //    if (E > Emax)
//                //    {
//                //        Emax = E;
//                //        bmpMax = bmp;
//                //    }
//                //}

//                _trackInfo.Tempo.SetValueForInterval(bmpMax, new TimeInterval(new TimeSpan(0), _GetCurrentTimeCallback()));

//                calculated = true;
//            }
//        }

//        public int DetectTempo(List<float[]> bandSignals, int minBpm, int maxBpm, int step, int maxFreq)
//        {
//            List<Complex[]> fftBands = new List<Complex[]>();
//            foreach (var bandSignal in bandSignals)
//            {
//                var fftBand = bandSignal.Select(x => new Complex() {X = x, Y = 0}).ToArray();
//                FastFourierTransform.FFT(true, (int)Math.Pow(fftBand.Length, 2), fftBand);

//                fftBands.Add(fftBand);
//            }

//            double eMax = 0f;
//            int bpmMax = 0;
//            int n = bandSignals[0].Length;

//            for (int bpm = minBpm; bpm <= maxBpm; bpm += step)
//            {
//                double e = 0;
//                int Ti = (int)(120.0 / bpm * maxFreq);

//                float[] l = new float[n];
//                float[] j = new float[n];
//                for (int k = 0; k < n; k += Ti)
//                {
//                    l[k] = j[k] = 1f;
//                }

//                Complex[] fftImpulses = new Complex[n];
//                for (int i = 0; i < n; i++)
//                {
//                    fftImpulses[i].X = (float)(l[i] * FastFourierTransform.HammingWindow(i, fftImpulses.Length));
//                    fftImpulses[i].Y = 0;// (float)(j[i] * FastFourierTransform.HammingWindow(i, fft.Length));
//                }

//                FastFourierTransform.FFT(true, (int)Math.Log(fftImpulses.Length, 2), fftImpulses);

//                for (int i = 0; i < fftBands.Count; i++)
//                {
//                    Complex[] fftBand = fftBands[i];
                    
//                    for (int k = 0; k < fftBand.Length; k++)
//                    {
//                        var x = fftBand[k].Multiply(fftImpulses[k]).Abs();
//                        e += (x*x);
//                    }
//                }

//                if (e > eMax)
//                {
//                    eMax = e;
//                    bpmMax = bpm;
//                }
//            }

//            return bpmMax;
//        }


//        public override void OnTrackStopped()
//        {
            
//        }
//    }

//    public static class CompexExtensions
//    {
//        public static Complex Multiply(this Complex c1, Complex c2)
//        {
//            return new Complex() {X = c1.X*c2.X - c1.Y*c2.Y, Y = c1.X*c2.Y + c2.X*c1.Y};
//        }

//        public static double Abs(this Complex c1)
//        {
//            return Math.Sqrt(c1.X*c1.X + c1.Y*c1.Y);
//        }
//    }
//}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NAudio.Dsp;

namespace Orpheus.Analyzer
{
    public class CombFilterTempoAnalyzer : TrackAnalyserBase
    {
        private List<Tuple<float, float>> samples;
        private bool calculated = false;

        public CombFilterTempoAnalyzer(TrackInfo trackInfo, GetCurrentTimeDelegate getCurrentTimeCallback)
            : base(trackInfo, getCurrentTimeCallback)
        {
            samples = new List<Tuple<float, float>>();
            _trackInfo.Tempo = new TimeStepFunction<double>();
        }

        bool IsPowerOfTwo(int x)
        {
            return (x & (x - 1)) == 0;
        }

        public override void ProcessSamples(float left, float right)
        {
            samples.Add(new Tuple<float, float>(left, right));
            int m = (int)Math.Log(samples.Count, 2);
            if (_GetCurrentTimeCallback().TotalSeconds > 5 && IsPowerOfTwo(samples.Count) && !calculated)
            {
                int N = samples.Count;
                Complex[] fft = new Complex[N];
                for (int i = 0; i < N; i++)
                {
                    fft[i].X = samples[i].Item1; // (float)(samples[i].Item1 * FastFourierTransform.HammingWindow(i, fft.Length));
                    fft[i].Y = 0;//(float)(samples[i].Item2 * FastFourierTransform.HammingWindow(i, fft.Length));
                }

                FastFourierTransform.FFT(true, m, fft);

                double Emax = 0;
                int bmpMax = 0;
                for (int bmp = 40; bmp <= 200; bmp += 10)
                {
                    int Ti = (int)(60.0 / bmp * 44100);

                    float[] l = new float[N];
                    float[] j = new float[N];
                    for (int k = 0; k < N; k+= Ti)
                    {
                        l[k] = j[k] = samples.Max(x => Math.Max(x.Item1, x.Item2));
                    }

                    Complex[] fftImpulses = new Complex[N];
                    for (int i = 0; i < N; i++)
                    {
                        fftImpulses[i].X = l[i]; // (float)(l[i] * FastFourierTransform.HammingWindow(i, fftImpulses.Length));
                        fftImpulses[i].Y = 0;// (float)(j[i] * FastFourierTransform.HammingWindow(i, fft.Length));
                    }

                    FastFourierTransform.FFT(true, m, fftImpulses);

                    double E = 0;
                    for (int i = 0; i < N; i++)
                    {
                        var x = fft[i].Multiply(fftImpulses[i]).Abs();
                        E += x * x;
                    }

                    if (E > Emax)
                    {
                        Emax = E;
                        bmpMax = bmp;
                    }
                }

                _trackInfo.Tempo.SetValueForInterval(bmpMax, new TimeInterval(new TimeSpan(0), _GetCurrentTimeCallback()));

                calculated = true;
            }
        }

        public override void OnTrackStopped()
        {

        }
    }

    public static class CompexExtensions
    {
        public static Complex Multiply(this Complex c1, Complex c2)
        {
            return new Complex() { X = c1.X * c2.X - c1.Y * c2.Y, Y = c1.X * c2.Y + c2.X * c1.Y };
        }

        public static double Abs(this Complex c1)
        {
            return Math.Sqrt(c1.X * c1.X + c1.Y * c1.Y);
        }
    }
}